<?php
declare(strict_types=1);

namespace App\Service\Web;

use App\Enum\Code;
use App\Exception\BusinessException;
use App\Mail\WelcomeMailWithText;
use App\Model\SystemLog;
use App\Model\User;
use App\Repository\Web\SystemLogsRepository;
use App\Repository\Web\UserRepository;
use App\Repository\Web\UserProfilesRepository;
use App\Security\LoginSecurity;
use Hyperf\DbConnection\Db;
use HyperfExt\Jwt\Contracts\JwtFactoryInterface;
use HyperfExt\Jwt\Contracts\ManagerInterface;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Di\Annotation\Inject;
use HyperfExt\Mail\Mail;

class UsersService
{

    protected RequestInterface $request;

    public function __construct(RequestInterface $request)
    {
        $this->request = $request;
    }

    #[Inject]
    private WelcomeMailWithText $mailService;
    #[Inject]
    private JwtFactoryInterface $jwtFactory;
    #[Inject]
    protected ManagerInterface $jwtManager;
    #[Inject]
    protected UserRepository $userRepository;
    #[Inject]
    protected UserProfilesRepository $userProfilesRepository;

    /**
     * @param array $data
     * @return array
     */
    public function register(array $data): array
    {
        $ip = $this->getClientIp();
        // 创建用户
        /** @var User $user */
        $user = $this->userRepository->create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => password_hash($data['password'], PASSWORD_BCRYPT),
            'last_login_ip' => $ip,
            'last_login_at' => time(),
            'avatar' => '',
        ]);
        $token = $this->generateToken($user);
        // 发送邮件
        $this->mailService->sendWelcomeEmail($user->email, $data['name']);

        return [
            // 'user' => $user,
            'token' => $token,
            'expires_in' => $this->jwtManager->getPayloadFactory()->getTtl()
        ];
    }

    private function generateToken(User $user): string
    {
        $jwt = $this->jwtFactory->make();
        // 添加类型标识到payload
        return $jwt->setCustomClaims(['type' => 'web', 'role' => $user->role, 'email' => $user->email])->fromSubject($user);
    }

    /**
     * 获取客户端IP
     */
    public function getClientIp(): string
    {
        $headers = $this->request->getHeader('x-forwarded-for');
        if (!empty($headers)) {
            return $headers[0];
        }
        $serverParams = $this->request->getServerParams();
        return $serverParams['remote_addr'] ?? '127.0.0.1';
    }

    public function refresh_token($token)
    {
        try {
            // 验证 refresh token
            $payload = $this->jwtFactory->make()->getPayload();

            $userId = $payload->get('sub');
            /** @var User $user */
            $user = $this->userRepository->find($userId);

            if (!$user) {
                throw new BusinessException(Code::Unauthorized, '用户不存在');
            }

            $this->jwtFactory->make()->setToken($token)->invalidate();

            $token = $this->generateToken($user);
            return [
                'token' => $token,
                'expires_in' => $this->jwtManager->getPayloadFactory()->getTtl()
            ];

        } catch (\Exception $e) {
            throw new BusinessException(Code::RequestParamError, $e->getMessage());
        }
    }

    public function login($data)
    {
        /** @var User $user */
        $user = $this->userRepository->findByEmail($data['email']);
        $loginSecurity = new LoginSecurity();
        if (!$user) {
            // 锁定ip
            if ($loginSecurity->checkLocked($this->getClientIp())) {
                throw new BusinessException(Code::Unauthorized, '此IP已被锁定，请稍后再试!');
            }
            throw new BusinessException(Code::Unauthorized, '邮箱不存在!');
        }

        if (!password_verify($data['password'], $user->password)) {
            // 锁定用户
            if ($loginSecurity->checkLocked($data['email'])) {
                throw new BusinessException(Code::Unauthorized, '此邮箱已被锁定，请稍后再试!');
            }
            throw new BusinessException(Code::Unauthorized, '密码错误!');
        }
        if ($user->status !== 'active') {
            throw new BusinessException(Code::Unauthorized, '账号已被禁用');
        }
        $loginSecurity->clearFailedAttempts($this->getClientIp());
        $loginSecurity->clearFailedAttempts($data['email']);

        // 记录登录时间及登录ip
        $this->userRepository->update($user->id, [
            'last_login_ip' => $this->getClientIp(),
            'last_login_at' => time()
        ]);

        $token = $this->generateToken($user);
        return [
            'token' => $token,
            'expires_in' => $this->jwtManager->getPayloadFactory()->getTtl()
        ];

    }

    public function user_profiles_view()
    {
        $payload = $this->jwtFactory->make()->getPayload();
        $userId = $payload->get('sub');
        $user = $this->userRepository->findWithProfile((int)$userId);

        if (!$user) {
            throw new BusinessException(Code::Unauthorized, '用户不存在');
        }
        return $user;
    }

    public function user_profiles_update(array $params)
    {
        $payload = $this->jwtFactory->make()->getPayload();
        $userId = $payload->get('sub');
        /**　@var User $user */
        $user = $this->userRepository->findWithProfile((int)$userId);

        if (!$user) {
            throw new BusinessException(Code::Unauthorized, '用户不存在');
        }

        $userData = $params['user'] ?? [];
        $profileData = $params['profiles'] ?? [];
        return Db::transaction(function () use ($userData, $profileData, $user) {
            if (!empty($userData)) {
                $email = $userData['email'] ?? '';
                if (!empty($email)) {
                    if ($this->userRepository->findByEmail($email)) {
                        throw new BusinessException(Code::RequestParamError, '邮箱更改失败,已存在该邮箱!');
                    }
                    // 发送邮箱
                    $this->mailService->sendChangeEmail($email, '');
                }
                $this->userRepository->update($user->id, $userData);
            }
            if (!empty($profileData)) {
                $this->userProfilesRepository->updateOrCreateProfile($user->id, $profileData);
            }
            return ['user' => $userData, 'profiles' => $profileData];
        });
    }
}